package com.maochd.cloud.common.datasource.util;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.IService;
import com.maochd.cloud.common.datasource.domain.BaseEntity;

import java.util.List;
import java.util.Objects;

/**
 * 业务相关工具类
 *
 * @author maochd
 * @since 2023/6/6 13:01
 */
@SuppressWarnings("UnusedReturnValue")
public class BizUtil<T extends BaseEntity, S extends IService<T>> {

    /**
     * biz service
     */
    private S ser;

    /**
     * entity class
     */
    private Class<T> clazz;

    /**
     * 常用字段名：编码
     */
    private final static String CODE = "code";

    /**
     * 常用字段名：行是否有效(0否1是)
     */
    private final static String ACTIVE = "active";

    /**
     * 初始化业务工具类
     *
     * @param ser biz service
     * @return BizUtil
     */
    public static <T extends BaseEntity, S extends IService<T>> BizUtil<T, S> of(S ser) {
        BizUtil<T, S> bu = new BizUtil<>();
        bu.ser = ser;
        bu.clazz = ser.getEntityClass();
        return bu;
    }

    /**
     * <p>检查业务数据是否存在</p>
     * 不存在则抛出异常
     *
     * @param id 主键
     * @throws RuntimeException biz
     */
    public BizUtil<T, S> notExistThrow(Long id) {
        getByIdIfExist(id);
        return this;
    }

    /**
     * 根据主键获取存在的数据对象
     *
     * @param id 主键
     * @return 数据对象
     * @throws RuntimeException biz
     */
    public T getByIdIfExist(Long id) {
        T en = ser.getById(id);
        BizUtil.notExistThrow(en);
        return en;
    }

    /**
     * 根据主键更新数据
     *
     * @param en 数据
     * @throws RuntimeException biz
     */
    public void updateById(T en) {
        ser.updateById(en);
    }

    /**
     * <p>逻辑删除</p>
     * 根据主键删除业务数据
     *
     * @param id 主键
     */
    public BizUtil<T, S> logicDel(Long id) {
        ser.update(updateExpress(BaseEntity::getId, id).set(BaseEntity::getActive, Boolean.FALSE));
        return this;
    }

    /**
     * <p>逻辑删除</p>
     * 根据其他主键关联删除业务数据
     *
     * @param sf 其他主键关联字段名
     * @param id 其他主键关联字段值
     */
    public <R> BizUtil<T, S> logicDel(SFunction<T, R> sf, Long id) {
        ser.update(updateExpress(sf, id).set(BaseEntity::getActive, Boolean.FALSE));
        return this;
    }

    /**
     * 批量新增
     *
     * @param dataList 数据列表
     */
    public BizUtil<T, S> saveBatch(List<T> dataList) {
        if (CollUtil.isEmpty(dataList)) {
            return this;
        }
        // 清空主键，防止重复
        dataList.forEach(d -> d.setId(null));
        ser.saveBatch(dataList);
        return this;
    }

    /**
     * <p>检查编码是否存在</p>
     * 根据主键（非必填）和编码
     *
     * @param id   主键（非必填）
     * @param code 编码
     */
    public Boolean checkCodeExist(Long id, String code) {
        LambdaQueryWrapper<T> qw = selectExpress()
                .eq(CODE, code).lambda()
                .ne(Objects.nonNull(id), BaseEntity::getId, id);
        return ser.count(qw) > 0;
    }

    /**
     * <p>检查编码是否存在</p>
     * 根据主键（非必填）和其他主键关联字段和编码
     *
     * @param id   主键（非必填）
     * @param code 编码
     * @param sf   其他主键关联字段名
     * @param val  其他主键关联字段值
     */
    public <R> Boolean checkCodeExist(Long id, String code, SFunction<T, R> sf, Object val) {
        LambdaQueryWrapper<T> qw = selectExpress()
                .eq(CODE, code).lambda()
                .eq(Objects.nonNull(val), sf, val)
                .ne(Objects.nonNull(id), BaseEntity::getId, id);
        return ser.count(qw) > 0;
    }

    /**
     * <p>查询列表</p>
     * 根据指定字段
     *
     * @param sf  指定字段名
     * @param val 指定字段值
     */
    public <R> List<T> list(SFunction<T, R> sf, Object val) {
        LambdaQueryWrapper<T> qw = selectExpress()
                .lambda()
                .ne(Objects.nonNull(val), sf, val);
        return ser.list(qw);
    }

    /**
     * 表达式：根据指定字段更新数据
     *
     * @param sf  字段名
     * @param val 字段值
     * @return 表达式
     */
    private <R> LambdaUpdateWrapper<T> updateExpress(SFunction<T, R> sf, Object val) {
        return Wrappers.<T>lambdaUpdate()
                .setEntityClass(clazz)
                .eq(Objects.nonNull(val), sf, val);
    }

    /**
     * 表达式：查询
     *
     * @return 表达式
     */
    private QueryWrapper<T> selectExpress() {
        return Wrappers.<T>query()
                .setEntityClass(clazz)
                .eq(ACTIVE, Boolean.TRUE);
    }

    /**
     * 数据不存在或已被删除
     *
     * @param biz 业务数据
     * @throws RuntimeException biz
     */
    public static void notExistThrow(BaseEntity biz) {
        if (Objects.isNull(biz)) {
            throw new RuntimeException("数据不存在！");
        }
        if (Boolean.FALSE.equals(biz.getActive())) {
            throw new RuntimeException("数据已被删除！");
        }
    }
}
